//头文件区
#include "bootloader.h"
#include "main.h"
//变量区
uint32_t app1_fault_num = 0, app2_fault_num = 0;//APP启动失败次数
uint32_t app1_version = 0, app2_version = 0;//APP版本号


// Bootloader初始化函数
void Bootloader_Init(void)
{
		HAL_GPIO_WritePin(LED_HeartBeat_GPIO_Port, LED_HeartBeat_Pin, GPIO_PIN_SET);
		LOG("BootLoader Start!\r\n");

		// 使用 volatile 关键字防止编译器优化掉内存访问
		// 使用 __IO 是 ST HAL 库中定义的标准写法，等同于 volatile

		app1_fault_num = *(__IO uint32_t*)(BOOT_PARAM_START_ADDRESS + 4 * BOOT_TO_APP1_FAULT);
		app2_fault_num = *(__IO uint32_t*)(BOOT_PARAM_START_ADDRESS + 4 * BOOT_TO_APP2_FAULT);

		app1_version = *(__IO uint32_t*)(BOOT_PARAM_START_ADDRESS + 4 * APP1_VERSION);
		app2_version = *(__IO uint32_t*)(BOOT_PARAM_START_ADDRESS + 4 * APP2_VERSION);

		if (app1_fault_num == 0xFFFFFFFF) app1_fault_num = 0;
		if (app2_fault_num == 0xFFFFFFFF) app2_fault_num = 0;

		if (app1_version == 0xFFFFFFFF) app1_version = 0;
		if (app2_version == 0xFFFFFFFF) app2_version = 0;
	
		LOG("app1_version  = 0x%08x   app2_version = 0x%08x\r\n",app1_version,app2_version);
		LOG("app1_fault_num  = 0x%08x   app2_fault_num = 0x%08x\r\n",app1_fault_num,app2_fault_num);
}

// Bootloader主流程函数，负责检查Bootloader标志并决定后续操作
void Bootloader_Run(void)
{
		uint32_t target_appAddress = APP1_START_ADDRESS;

		if (app1_fault_num >= 3){
				LOG("Boot to APP1 fault times >= 3, BootLoader jump to APP2 directly!\r\n");
				
				app1_fault_num = 0;
				app1_version = 0;
				app2_fault_num++;
				target_appAddress = APP2_START_ADDRESS;
				
		}
		else if (app2_fault_num >= 3){
				LOG("Boot to APP2 fault times >= 3, BootLoader jump to APP1 directly!\r\n");
			
				app2_fault_num = 0;
				app2_version = 0;
				app1_fault_num++;
				target_appAddress = APP1_START_ADDRESS;
		}
		else{
				if(app1_version>= app2_version){
						app2_fault_num = 0;
						app1_fault_num++;
						target_appAddress = APP1_START_ADDRESS;
						LOG("Boot to APP1!\r\n");
				}else{
						app1_fault_num = 0;
						app2_fault_num++;
						target_appAddress = APP2_START_ADDRESS;
						LOG("Boot to APP2!\r\n");
				}
		}
		
		APP_Flash_Erase(BOOT_PARAM_SECTOR);
		APP_Flash_Write(BOOT_PARAM_START_ADDRESS + 4 * BOOT_TO_APP1_FAULT,(uint8_t *)&app1_fault_num,sizeof(app1_fault_num));
		APP_Flash_Write(BOOT_PARAM_START_ADDRESS + 4 * BOOT_TO_APP2_FAULT,(uint8_t *)&app2_fault_num,sizeof(app2_fault_num));
		APP_Flash_Write(BOOT_PARAM_START_ADDRESS + 4 * APP1_VERSION,(uint8_t *)&app1_version,sizeof(app1_version));
		APP_Flash_Write(BOOT_PARAM_START_ADDRESS + 4 * APP2_VERSION,(uint8_t *)&app2_version,sizeof(app2_version));
		LOG("target_appAddress = 0x%08x\r\n",target_appAddress);
    JumpToApplication(target_appAddress);
}

// 跳转到应用程序的函数，实现从Bootloader跳转到指定地址的应用程序的功能
// 参数appAddress是要跳转到的应用程序的起始地址
void JumpToApplication(uint32_t appAddress)
{
    typedef void (*pFunction)(void);
    pFunction Jump_To_Application;

    // 关闭全局中断，防止在跳转过程中出现意外的中断干扰
    __disable_irq();

    // 复位向量表指向应用程序地址，将系统控制块（SCB）的向量表偏移寄存器（VTOR）设置为指定的应用程序地址
    SCB->VTOR = appAddress;

    // 获取应用程序复位处理函数地址，从应用程序向量表中的复位向量位置（地址 + 4 处）获取函数指针
    Jump_To_Application = (pFunction) * (__IO uint32_t *)(appAddress + 4);

    // 清除相关的系统控制块标志位等（可选操作，根据实际情况决定是否需要）
    __set_MSP(*(__IO uint32_t *)appAddress);
    // 跳转到应用程序复位处理函数，开始执行应用程序
    Jump_To_Application();
}

// 用于擦除应用程序所在Flash扇区的函数
// 参数Sector是要擦除的Flash扇区编号
void APP_Flash_Erase(uint32_t Sector)
{
    HAL_StatusTypeDef status;
    uint32_t pageError;

    FLASH_EraseInitTypeDef eraseInitStruct;

    // 设置擦除类型为按扇区擦除
    eraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;
    // 指定要擦除的Flash存储体为Bank 1
    eraseInitStruct.Banks = FLASH_BANK_1;
    // 将传入的参数Sector赋值给结构体成员，确定要擦除的具体扇区
    eraseInitStruct.Sector = Sector;
    // 表示此次擦除操作只针对1个扇区进行
    eraseInitStruct.NbSectors = 1;
    // 设置擦除操作对应的电压范围为3
    eraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3;

    // 解锁Flash
    status = HAL_FLASH_Unlock();
    if (status!= HAL_OK)
    {
        LOG("Flash Unlock Error\r\n");
        return;
    }

    //执行Flash擦除操作
    status = HAL_FLASHEx_Erase(&eraseInitStruct, &pageError);
    if (status!= HAL_OK)
    {
        LOG("Flash Erase Error, pageError = %u\r\n",pageError);
        HAL_FLASH_Lock();
        return;
    }

    // 擦除操作完成后，锁定Flash
    HAL_FLASH_Lock();
}

// 用于向Flash中写入应用程序固件数据的函数
// 参数APP_FLAG_ADDRESS是要写入的起始地址，firmware_buffer是指向固件数据缓冲区的指针，firmware_size是固件数据的大小（字节数）
void APP_Flash_Write(uint32_t APP_FLAG_ADDRESS,uint8_t *firmware_buffer,uint32_t firmware_size)
{
    HAL_StatusTypeDef status;

    // 解锁Flash
    status = HAL_FLASH_Unlock();
    if (status!= HAL_OK)
    {
        LOG("Flash Unlock Error\r\n");
        return;
    }

    // 通过循环逐个字节地将固件数据写入Flash中
    for (uint32_t i = 0; i < firmware_size; i++)
    {
        // 调用HAL_FLASH_Program函数以字节为单位进行写入操作
        status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, APP_FLAG_ADDRESS + i, firmware_buffer[i]);
        if (status!= HAL_OK)
        {
            LOG("Flash Program Error at address 0x%X\r\n", APP_FLAG_ADDRESS + i);
            HAL_FLASH_Lock();
            return;
        }
    }

    // 所有字节数据写入完成后，锁定Flash，防止数据异常
    HAL_FLASH_Lock();
}

